//
//  ODBBluetoothPeripheral.swift
//  OBD
//
//  Created by sqkj on 2021/9/29.
//

import Foundation
import UIKit
import CoreBluetooth
import CryptoSwift
import SGSelectionModal

class OBDBluetoothPeripheral: NSObject {
    var peripheralName: String = ""
    var peripheralSN: String?

    var serialNumber: UInt64 = 0x0001
    
    var peripheral:CBPeripheral
    var isRady: Bool = false //设备是否处于就绪状态
    
    /// Intance of CentralManager which is used to the bluetooth communication
    //public unowned let manager: CBCentralManager
    
    /// Advertisement data of scanned peripheral
    public let advertisementData: [String: Any]
    
    /// Scanned peripheral's RSSI value.
    public let rssi: NSNumber
    
    public var reciveBytes: [UInt8] = []
    private var queueBytes: [UInt8] = []
    
    //设备中的所有服务
    public var serviceDic: [String:CBService] = [:]
    
    //默认的写特征值和读特征值 默认为nil
    public var characteristicWrite: CBCharacteristic?
    public var characteristicNotify: CBCharacteristic?
    
    //特征值列表
    public var characteristicDic: [CBService:[CBCharacteristic]] = [:]

    //单次传输数据包 单位字节
    //let MAX_COUNT = 2048
    
    //AMRK: - 蓝牙使用方法
    
    /// 向蓝牙外设发送数据 向指定的serviceUUID和 writeUUID发数据
    /// - Parameter data: 待发送的数据
    /// - Parameter serviceUUID: serviceUUID
    /// - Parameter writeUUID: writeUUID
    /// - Parameter type: 发送需不需要回复该消息是否发送成功
    func send(data: Data,
              serviceUUID:String?,
              writeUUID:String?,
              type:CBCharacteristicWriteType?,
              completionHandler:BLESendDataCompletionHandler?)
    {

        let writeType = type == nil ? ODBBLEManager.configuration.writeType : type

        //没有符合条件的特征值则直接返回
        let characteristic = searchWriteCharacteristic(serviceUUID: serviceUUID, writeUUID: writeUUID)
        guard let _ = characteristic else{
            completionHandler?(self,data,OBDBluetoothError.invalidCharacteristic)
            return
        }
        completionHandler?(self,data,nil)

        DispatchQueue.global().async {
            self.sendAsync(data: data, characteristic: characteristic!,type:writeType!)
        }
    }

    /// 订阅蓝牙外设
    /// - Parameter serviceUUID: serviceUUID
    /// - Parameter notifyUUID: notifyUUID
 
    func notify(serviceUUID:String?,notifyUUID:String?, completionHandler: BLENotifyCharacteristicCompletionHandler?){
        let characteristic = searchNotifyCharacteristic(serviceUUID: serviceUUID, notifyUUID: notifyUUID)
        
        //没有符合条件的特征值则直接返回
        guard let _ = characteristic else{
            completionHandler?(self,nil, BLEHandlerResult.failure(.invalidCharacteristic))
            return
        }
        completionHandler?(self,characteristic, BLEHandlerResult.success)
        peripheral.setNotifyValue(true, for: characteristic!)
    }
    


    //传递数据
    func sendAsync(data: Data, characteristic: CBCharacteristic,type:CBCharacteristicWriteType) {
        var data = data
        //本设备单次传输最大字节值 ，跟手机型号有关，不同手机型号各不相同
        let mtu:Int = self.peripheral.maximumWriteValueLength(for: type)
        //如果单次数据过大则分开传递
        var bufferData:[UInt8] = []
        while data.count > 0 {
            while bufferData.count < mtu && data.count > 0 {
                bufferData.append(data.removeFirst())
            }
            self.peripheral.writeValue(Data(bufferData), for: characteristic,type: type)
        }
    }
    
    //MARK: Characteristic
    /// 根据service 和writeUUID 找出特征值
    /// - Parameter serviceUUID: serviceUUID
    /// - Parameter writeUUID: 写的UUID
    func searchWriteCharacteristic(serviceUUID:String? = nil , writeUUID:String? = nil) -> CBCharacteristic?{
        guard writeUUID != nil else {
            return characteristicWrite
        }
        return  searchCharacteristic(serviceUUID: serviceUUID, operationUUID: writeUUID!)
    }
     
    /// 根据service 和notifyUUID 找出特征值
    /// - Parameter serviceUUID: serviceUUID
    /// - Parameter notifyUUID: 读的UUID
    func searchNotifyCharacteristic(serviceUUID:String? = nil , notifyUUID:String? = nil) -> CBCharacteristic?{
        guard notifyUUID != nil else {
            return characteristicNotify
        }
        return  searchCharacteristic(serviceUUID: serviceUUID, operationUUID: notifyUUID!)
    }
    
    /// 根据service 和特征值UUID 找出特征值
    /// - Parameter serviceUUID: serviceUUID
    /// - Parameter operationUUID: 读或写的UUID
    func searchCharacteristic(serviceUUID:String? = nil , operationUUID:String) -> CBCharacteristic?{
        var characteristicArray:[CBCharacteristic]
        //如果serviceUUID有值，则只取对应的uuid的service内特征值做比对
        if let serviceUUID = serviceUUID{
            let service = serviceDic[serviceUUID]!
            characteristicArray = characteristicDic[service]!
        }else {
            //否则取出所有的service的特征值比对
            characteristicArray = characteristicDic.flatMap({ $0.value})
        }
        //从储存的特征值列表中根据给定特征值的UUID找出符合的特征值
        return  characteristicArray.filter { $0.uuid == CBUUID(string: operationUUID)}.first
    }
    
    init(_ peripheral:CBPeripheral,_ advertisementData:[String:Any] ,_ rssi: NSNumber) {
        self.peripheral = peripheral
        self.advertisementData = advertisementData
        self.rssi = rssi
        self.peripheralName = peripheral.name ?? ""
        super.init()
        self.peripheral.delegate = self
    }
}

extension OBDBluetoothPeripheral:CBPeripheralDelegate{
    //发现服务
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        for service in peripheral.services! {
            BQPrint("外设中的服务有\(service)")
            serviceDic[service.uuid.uuidString] = service
            // 设备搜索所有服务中的特征值
            peripheral.discoverCharacteristics(nil, for: service)
            
//            //只对需要的特征值搜索
//            if let writeUUID = OBDBluetooth.characteristicWriteUUID,
//                let NotifyUUID = OBDBluetooth.characteristicNotifyUUID{
//                let CBUUIDArray = [CBUUID(string:writeUUID),CBUUID(string: NotifyUUID)]
//                peripheral.discoverCharacteristics(CBUUIDArray, for: service)
//            }
        }
    }
    
    //发现特征
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        var array: [CBCharacteristic] = []
        
        for characteristic in service.characteristics! {
            BQPrint("外设中的特征有：\(characteristic)")
            //记录写特征
            if let UUID = ODBBLEManager.configuration.characteristicWriteUUID{
                if characteristic.uuid == UUID {
                    characteristicWrite = characteristic
                }
            }
            //记录读特征
            if let UUID = ODBBLEManager.configuration.characteristicNotifyUUID{
                if characteristic.uuid == UUID {
                    characteristicNotify = characteristic
                    // 订阅
                    peripheral.setNotifyValue(true, for: self.characteristicNotify!)
                }
            }
            //记录所有的特征值
            array.append(characteristic)
        }
        
        characteristicDic[service] = array
    }

    //订阅状态
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {

        if let error = error {
            BQPrint("订阅失败: \(error)")
            return
        }
        
        if characteristic.isNotifying {
            BQPrint("订阅成功")
            for delegate in ODBBLEManager.channels.delegateArray {
                isRady = true
                delegate.bluetoothPeripheralReady?(peripheral: peripheral)
            }
            ODBBLEManager.bluetoothChannel.currentChannelCallback().blockOnPeripheralReady?(peripheral)
        } else {
            for _ in ODBBLEManager.channels.delegateArray {
            }
            BQPrint("取消订阅")
        }
    }
    
    
    // 接收到数据
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        BQPrint("\(peripheral.name ?? "nil name") 接受返回数据")        
        let value = [UInt8](characteristic.value!)
        reciveBytes.append(contentsOf: value)
        print(reciveBytes.toHexString())
        let data = Data(bytes: reciveBytes, count: reciveBytes.count)
        ODBBLEManager.channels.delegateArray.forEach { (delegate) in
            delegate.bluetoothPeripheral?(peripheral, didReadData: data)
        }
        ODBBLEManager.bluetoothChannel.currentChannelCallback().blockOnBluetoothReadData?(peripheral,data)
        queueBytes.removeAll()
        /*
        while reciveBytes.count > 0 {
            if queueBytes.first != 0x55 {
                queueBytes.removeAll()
            }
            let bufferI = reciveBytes.first
            queueBytes.append(bufferI!)
            if bufferI == 0x55 && queueBytes.count > 1 && reciveBytes.count > 1 && reciveBytes[1] == 0xAA {
                queueBytes.removeLast()
                let verifyOK = verifyValue()
                if verifyOK {
                    
                }
                continue
            }
            
            if bufferI == 0xAA && queueBytes.count > 5 && [queueBytes[queueBytes.count-2],queueBytes.last].elementsEqual([0x55, 0xAA]) {
                queueBytes.removeLast()
                queueBytes.removeLast()
                let verifyOK = verifyValue()
                if verifyOK {
                    queueBytes.append(0x55)
                }
                continue
            }
            
            reciveBytes.removeFirst()
        }*/
    }
    
    func verifyValue() -> Bool {
        // 验证数据完整性
        print("正准备验证的收到的数据",queueBytes,#function)
        print(queueBytes.toHexString())
        let verifyOK = Utils.verify(bytes: queueBytes)
        if verifyOK {
            print("队列中验证通过收到的数据",queueBytes,"\n 缓冲区剩余的bytes",reciveBytes,#function)
     
            
            let data = Data(bytes: queueBytes, count: queueBytes.count)
            ODBBLEManager.channels.delegateArray.forEach { (delegate) in
                delegate.bluetoothPeripheral?(peripheral, didReadData: data)
            }
            ODBBLEManager.bluetoothChannel.currentChannelCallback().blockOnBluetoothReadData?(peripheral,data)
            
            queueBytes.removeAll()
            return true
        }else {
            print("数据验证失败",queueBytes)
            queueBytes.removeAll()
            return false
        }
    }
    
    
    
    
    //写入数据响应
    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
        BQPrint("\(peripheral.name ?? "nil name") 写入数据")
    }
}
